package club.mzywucai.blog.air_portal.service.impl;

import club.mzywucai.blog.air_portal.pojo.domain.CreateNewFileCode;
import club.mzywucai.blog.air_portal.pojo.domain.FileParam;
import club.mzywucai.blog.air_portal.pojo.domain.JsonData;
import club.mzywucai.blog.air_portal.pojo.domain.MakeDir;
import club.mzywucai.blog.air_portal.pojo.po.FileInfoPo;
import club.mzywucai.blog.air_portal.pojo.po.UploadResult;
import club.mzywucai.blog.air_portal.service.AddFileInfoService;
import club.mzywucai.blog.air_portal.service.UploadService;
import club.mzywucai.blog.air_portal.util.SizeToStringUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Data;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadBase;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.commons.io.FileUtils;
import org.apache.tomcat.jni.FileInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * @author mzywucai
 * @Description 上传相关的功能
 * @date 2019/6/3
 */
@Data
@PropertySource({"classpath:config/application.properties"})
@Service
public class UploadServiceImpl implements UploadService {
    @Autowired
    AddFileInfoService addFileInfoService;

    /**
     * @Description 缓存中间目录的位置
     */
    @Value("${server.system.tmp}")
    private String tmpPath;
    /**
     * @Description 最大的文件大小：单位B
     */
    @Value("${file.max-size}")
    private Long maxSize;

    @Autowired
    MakeDir mkdir;

    @Autowired
    CreateNewFileCode createNewFileCode;

    @Transactional(propagation= Propagation.REQUIRED)
    @Override
    public JsonData upload(HttpServletRequest request, HttpServletResponse response) {

        try {
            request.setCharacterEncoding("utf-8");
            response.setContentType("text/html;charset=utf-8");

            // 通用用户拦截，先去。

            File tmp = new File(tmpPath);

            DiskFileItemFactory factory = new DiskFileItemFactory(10*1024, tmp);
            ServletFileUpload upload = new ServletFileUpload(factory);

            // 这里没有header了。
            // upload.setHeaderEncoding("utf-8");
            // String type = request.getHeader("Type");

            // 一个request中最大的容量为maxSize
            upload.setSizeMax(maxSize);
            // 单个文件的最大容量为maxSize
            upload.setFileSizeMax(maxSize);
            // 解析request
            List<FileItem> fileItems = upload.parseRequest(request);

            if(fileItems != null) {
                FileParam fileParam = analyseFileItems(fileItems, request);
                UploadResult uploadResult = saveToDbAndGetResult(fileParam, request);

                return JsonData.buildSuccess(0, uploadResult, "success");
            } else {
                return JsonData.buildError("empty file items.");
            }


        } catch (FileUploadBase.FileSizeLimitExceededException e) {
            // 捕获单个文件大小超规定范围异常
            return JsonData.buildError("您上传的文件大小超过规定："+ SizeToStringUtil.getSizeString(maxSize));

        } catch (FileUploadBase.SizeLimitExceededException e) {
            // 捕获总文件大小超规定范围异常
            return JsonData.buildError("您上传的文件大小超过规定："+ SizeToStringUtil.getSizeString(maxSize));

        } catch (Exception e) {
            // server load fail. exception
            return JsonData.buildError("server load fail.");
        }
    }

    /**
     * @Description 调用service加入FileInfoPo，并返回UploadResult.
     * @author mzywucai
     * @time 2019-06-04
     * @see 
     * @return   club.mzywucai.blog.air_portal.pojo.po.UploadResult
     * @params  []
     */
    private UploadResult saveToDbAndGetResult(FileParam fileParam, HttpServletRequest request){
        String fileCode = createNewFileCode.getFileCode();

        setSessionCodeList(request, fileCode);

        FileInfoPo fileInfoPo = packageFileInfoPo(fileParam, fileCode);

        // 调用service层，插入字段
        addFileInfoService.addFile(fileInfoPo);

        UploadResult uploadResult = packageUploadResult(fileParam, fileCode);

        return uploadResult;
    }


    /**
     * @Description 封装返回的结果集
     * @author mzywucai
     * @time 2019-06-04
     * @see 
     * @return   club.mzywucai.blog.air_portal.pojo.po.UploadResult
     * @params  [fileParam, fileCode]
     */
    private UploadResult packageUploadResult(FileParam fileParam, String fileCode) {
        UploadResult uploadResult = new UploadResult();
        // origin
        uploadResult.setOriginName(fileParam.getFileName());
        // new
        uploadResult.setNewName(fileParam.getNewName());
        // suffix
        uploadResult.setSuffix(fileParam.getSuffix());
        // fileUrl
        uploadResult.setUrl(fileParam.getFileURL());
        // fileUri
        uploadResult.setUri(fileParam.getFileURI());
        // fileCode
        uploadResult.setFileCode(fileCode);

        return uploadResult;
    }

    /**
     * @Description 封装fileInfoPo
     * @author mzywucai
     * @time 2019-06-04
     * @see 
     * @return   club.mzywucai.blog.air_portal.pojo.po.FileInfoPo
     * @params  [fileParam, fileCode]
     */
    private FileInfoPo packageFileInfoPo(FileParam fileParam, String fileCode){
        FileInfoPo fileInfoPo = new FileInfoPo();
        fileInfoPo.setFileNewName(fileParam.getNewName());
        fileInfoPo.setFileOriginName(fileParam.getFileName());

        fileInfoPo.setFileURI(fileParam.getFileURI());
        fileInfoPo.setFilePath(fileParam.getPath());

        fileInfoPo.setFileSuffix(fileParam.getSuffix());
        fileInfoPo.setFileSize(fileParam.getFileSize());

        fileInfoPo.setFileType(fileParam.getFileType());
        fileInfoPo.setFileURL(fileParam.getFileURL());

        fileInfoPo.setFilePass(fileParam.getFilePass());
        fileInfoPo.setFileTimes(fileParam.getFileTimes());

        fileInfoPo.setFileCode(fileCode);

        return fileInfoPo;
    }

    /**
     * @Description 设置session中的codeList，并返回当前文件的code
     * @author mzywucai
     * @time 2019-06-04
     * @see 
     * @return   java.lang.String
     * @params  [request]
     */
    private void setSessionCodeList(HttpServletRequest request, String fileCode) {

        List<String> fileCodeList;
        HttpSession session = request.getSession();

        Object obj = session.getAttribute("fileCodeList");

        if (null != obj) {
            fileCodeList = (List<String>) session.getAttribute("fileCodeList");
        } else {
            fileCodeList = new ArrayList<String>();
        }

        fileCodeList.add(fileCode);

        if (fileCodeList.size() == 6) {
            fileCodeList.remove(0);
        }

        session.setAttribute("fileCodeList", fileCodeList);

    }

    /**
     * @Description 分析FileItems
     * @author mzywucai
     * @time 2019-06-03
     * @see
     * @return   club.mzywucai.blog.air_portal.pojo.domain.JsonData
     * @params  [fileItems]
     */
    private FileParam analyseFileItems(List<FileItem> fileItems, HttpServletRequest request) throws IOException {
        FileParam fileParam = new FileParam();

        for(FileItem item: fileItems) {

            if(item.isFormField()) {
                // 文件外，字符串字段判断
                if(item.getFieldName().equals("fileName")) {
                    fileParam.setFileName(item.getString("utf-8"));
                }
                if(item.getFieldName().equals("fileSize")) {
                    fileParam.setFileSize(item.getString("utf-8"));
                }
                if(item.getFieldName().equals("fileType")) {
                    fileParam.setFileType(item.getString("utf-8"));
                }
                if(item.getFieldName().equals("filePass")) {
                    fileParam.setFilePass(item.getString("utf-8"));
                }
                if(item.getFieldName().equals("fileTimes")) {
                    fileParam.setFileTimes(Integer.parseInt(item.getString("utf-8")));
                }
            } else {
                /*
                 * 使用UUID算法，生成唯一的文件名
                 * 避免文件覆盖的问题！
                 */
                String uuid = UUID.randomUUID().toString();
                // get到真正的文件名！
                String originName = item.getName();
                // 获取到后缀名 考虑.tar.gz类似后缀名
                fileParam.setSuffix(getSuffix(originName));
                // 组合新的文件名
                fileParam.setNewName(uuid + fileParam.getSuffix());

                // 位置：URI+文件名
                fileParam.setFileURL(getHost(request));
                // 获得一直到文件底层目录的URI、URL、PATH。
                fileParam.setFileURI("/" + mkdir.getTodayFileURI() + fileParam.getNewName());
                fileParam.setFileURL(getHost(request) + mkdir.getTodayFileURI() + fileParam.getNewName());
                fileParam.setPath(mkdir.getTodayFilePath() + fileParam.getNewName());

                // 生成目录结构
                File file = new File(fileParam.getPath());

                //开始文件传输
                FileUtils.copyInputStreamToFile(item.getInputStream(), file);

                // 删除item传输的tmp临时文件
                item.delete();
            }
        }

        return fileParam;
    }


    /**
     * @Description 通过request获取host
     * @author mzywucai
     * @time 2019-06-03
     * @see 
     * @return   java.lang.String
     * @params  []
     */
    private String getHost(HttpServletRequest request) {
        String uri = request.getRequestURI();
        String url = request.getRequestURL().toString();
        String host = url.replace(uri, "/");

        return host;
    }

    /**
     * @Description 手动识别后缀
     * @author mzywucai
     * @time 2019-01-31
     * @see
     * @return   java.lang.String
     * @params  [origin]
     */
    private String getSuffix(String origin) {
        String suffix;
        if (origin.contains(".tar")) {
            suffix = origin.substring(origin.lastIndexOf(".tar"));

        } else if(origin.contains(".")) {
            suffix = origin.substring(origin.lastIndexOf("."));
        } else {
            suffix = "";
        }

        return suffix;
    }
}
